package com.zzzy.framework.interceptor;

import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.zzzy.framework.common.enums.sys.DataScopeEnum;
import com.zzzy.framework.common.util.DataPermissionHolder;
import com.zzzy.framework.common.util.SecurityUtils;
import com.zzzy.framework.dto.DataPermissionDto;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Description: 数据权限拦截器
 *
 * @author fc
 * @date 2024/5/21
 */
public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
            return;
        }
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        mpBs.sql(this.parserSingle(mpBs.sql(), ms.getId()));
    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        PlainSelect plainSelect = select.getPlainSelect();
        plainSelect.setWhere(getWhere((Table) plainSelect.getFromItem(), plainSelect.getWhere()));
    }

    @Override
    protected void processDelete(Delete delete, int index, String sql, Object obj) {
        delete.setWhere(getWhere(delete.getTable(), delete.getWhere()));
    }

    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        update.setWhere(getWhere(update.getTable(), update.getWhere()));
    }

    private Expression getWhere(Table table, Expression where) {
        if (!DataPermissionHolder.getIsFilterData() || Objects.isNull(table)) {
            return where;
        }
        DataPermissionDto dataPermissionDto = SecurityUtils.getLoginUser().getDataPermissionDto();
        //全部数据权限 不过滤数据
        if (DataScopeEnum.ALL.getValue().equals(dataPermissionDto.getDataPermission())){
            return where;
        }
        // 有别名用别名，无别名用表名，防止字段冲突报错
        String mainTableName = table.getAlias() == null ? table.getName() : table.getAlias().getName();
        Set<Long> userIdSet = dataPermissionDto.getUserIdSet();
        if (userIdSet.size() == 1){
            EqualsTo equalsTo = new EqualsTo();
            equalsTo.setLeftExpression(new Column(mainTableName + ".user_id"));
            equalsTo.setRightExpression(new Column(String.valueOf(userIdSet.iterator().next())));
            return buildExpression(where, equalsTo);
        } else {
            InExpression inExpression = new InExpression();
            inExpression.setLeftExpression(new Column(mainTableName + ".user_id"));
            String inSql = userIdSet.stream().map(String::valueOf).collect(Collectors.joining(","));
            inExpression.setRightExpression(new Column("(" + inSql + ")"));
            return buildExpression(where, inExpression);
        }
    }

    private Expression buildExpression(Expression where, Expression otherWhere) {
        if (Objects.isNull(where)){
            return otherWhere;
        } else {
            return new AndExpression(where, otherWhere);
        }
    }
}